using System;
using System.Data;
using System.Data.OracleClient;

namespace uk.co.tonyditchfield.tort.utility.conversion
{
    /// <summary>
    /// Utility class to convert native types to respective db types.
    /// </summary>
    internal class DbTypeConverter
    {
        private Type type;
        private DbType dbType;
        private SqlDbType sqlDbType;
        private OracleType oracleType;
        private DbTypeMapping mapping = null;

        /// <summary>
        /// Converts a native type to generic DBType. Only to be used if the real DB engine is not known.
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        /// <exception cref="PersistenceException"></exception>
        internal DbType ConvertToDbType(object obj)
        {
            Convert(obj.GetType());
            if (!mapping.DbTypeSet)
            {
                throw new exception.PersistenceException(obj.GetType().Name + "not supported!");
            }
            return dbType;
        }

        /// <summary>
        /// Converts a native type to a Sql Server Db Type
        /// </summary>
        /// <param name="nativeType"></param>
        /// <returns></returns>
        /// <exception cref="PersistenceException"></exception>
        internal SqlDbType ConvertToSqlDbType(Type nativeType)
        {
            Convert(nativeType);
            if (!mapping.SqlDbTypeSet)
            {
                throw new exception.PersistenceException(nativeType.Name + " not supported!");
            }
            return sqlDbType;
        }

        /// <summary>
        /// Converts a native type to a Oracle Db Type
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        /// <exception cref="PersistenceException"></exception>
        internal OracleType ConvertToOracleDbType(Type nativeType)
        {
            Convert(nativeType);
            if (!mapping.OracleDbTypeSet)
            {
                throw new exception.PersistenceException(nativeType.Name + " not supported!");
            }
            return oracleType;
        }

        internal Type ConvertToNativeType(SqlDbType sqlDbType)
        {
            Convert(sqlDbType);
            if (!mapping.NativeTypeSet)
            {
                throw new exception.PersistenceException(sqlDbType.ToString() + " not supported!");
            }
            return type;
        }

        private void Convert(Type type)
        {
            foreach (DbTypeMapping mapping in DbTypeMappingCollection.DbTypeMappings())
            {
                if (type == mapping.NativeType)
                {
                    if (mapping.DbTypeSet)
                    {
                        dbType = mapping.DbType;
                        this.mapping = mapping;
                    }
                    if (mapping.SqlDbTypeSet)
                    {
                        sqlDbType = mapping.SqlDbType;
                        this.mapping = mapping;
                    }
                    if (mapping.DbTypeSet)
                    {
                        oracleType = mapping.OracleType;
                        this.mapping = mapping;
                    }
                }
            }
        }

        private void Convert(SqlDbType sqlDbType)
        {
            foreach (DbTypeMapping mapping in DbTypeMappingCollection.DbTypeMappings())
            {
                if (mapping.SqlDbType == sqlDbType)
                {
                    type = mapping.NativeType;
                    this.mapping = mapping;
                }
            }
        }
    }
}
